﻿//AcademicPlanner - course registration planning web app.
//Copyright (C) 2009  Boubacar Diallo

//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.


using System;
using System.Collections.Generic;
using System.Linq;
using AcademicPlanner.Domain.Models;
using AcademicPlanner.Framework.Helpers;

namespace AcademicPlanner.Domain.Components
{
	/// <summary>
	/// MembershipMngr is the component of the AcademicPlanner application
	/// responsible for registration and membership.
	/// </summary>
	/// <since>1.0</since>
	public class MembershipMngr : Component
	{
		private MembershipMngr() { }

		private class InstanceHolder
		{
			internal static MembershipMngr INSTANCE = new MembershipMngr();
		}


		/// <summary>
		/// Gets the single MembershipMngr intance.
		/// </summary>
		/// <returns>The single MembershipMngr intance.</returns>
		/// <since>1.0</since>
		internal static MembershipMngr GetInstance()
		{
			return InstanceHolder.INSTANCE;
		}


		/// <summary>
		/// Registers someone into the system. This method effectively
		/// creates a new <c>Person</c>, persist him to the data storage
		/// and sends out an email with a link to visit in order to finalize
		/// the registration.
		/// </summary>
		/// <param name="pEmail">The email address of the registeree.</param>
		/// <param name="pCompleRegistrationUrlFormat">
		/// A format of the link sent to the registeree in order to complete the registration;
		/// {0} is assumed for the registeree email address,
		/// {1} is assumed for the verification token,
		/// {2} is assumed for the new <c>Person</c>'s identifier.
		/// </param>
		/// <remarks>
		/// <c>NotCriticalDomainException</c>s may be thrown in case of biz logic violations
		/// such as a <c>Person</c> with the given email address already existing.
		/// <c>InvalidModelPropertiesException</c>s may be thrown in case of validation errors
		/// such as invalid email address given.
		/// <c>NotAuthorizedException</c>s may be thrown in case of access control
		/// restrictions.
		/// </remarks>
		/// <since>1.0</since>
		public void RegisterPerson(string pEmail, string pCompleRegistrationUrlFormat)
		{
			var vTask = new RegisterTask(pEmail, pCompleRegistrationUrlFormat);
			vTask.Run();
		}

		/// <summary>
		/// Helps a <c>Person</c> recover his <c>Account</c>.
		/// </summary>
		/// <remarks>
		/// This method verifies that a <c>Person</c> with the given
		/// email address exists and has at least one <c>Account</c>;
		/// it then sends him an email with instructions and a link
		/// to follow.
		/// </remarks>
		/// <param name="pEmail">The email address of the <c>Person</c>.</param>
		/// <param name="pRecoverAccountUrlFormat">
		/// A format of the link to follow sent in the email;
		/// {0} is assumed for the <c>Person</c>'s email address,
		/// {1} is assumed for the verification token,
		/// {2} is assumed for the <c>Person</c>'s identifier.
		/// </param>
		/// <exception cref="T:AcademicPlanner.Domain.NotCriticalDomainException">
		/// Thrown with error code <c>DomainErrorCode.C_PERSON_TO_HELP_RECOVER_ACCOUNT_NOT_FOUND</c>
		/// if the <c>Person</c> with the given email address does not exist.
		/// </exception>
		/// <exception cref="T:AcademicPlanner.Domain.NotCriticalDomainException">
		/// Thrown with error code <c>DomainErrorCode.C_ACCOUNT_RECOVERY_EMAIL_FAILURE</c>
		/// if a technical error occured while sending out the email.
		/// </exception>
		/// <since>1.0</since>
		public void HelpRecoverAccount(string pEmail, string pRecoverAccountUrlFormat)
		{
			var vPers = DomainMaster.MainModel.AllPeople.WithEmail(pEmail);

			if (vPers == null || vPers.Accounts.ToList().Count <= 0)
				throw new NotCriticalDomainException(DomainErrorCode.C_PERSON_TO_HELP_RECOVER_ACCOUNT_NOT_FOUND);

			if (!SendAccountRecoveryEmail(vPers, pRecoverAccountUrlFormat))
				throw new NotCriticalDomainException(DomainErrorCode.C_ACCOUNT_RECOVERY_EMAIL_FAILURE);
		}

		bool SendAccountRecoveryEmail(Person pPerson, string pRecoverAccountUrlFormat)
		{
			DomainMaster.Auth.EnterPrivelegedAuthorizationMode();

			var vFrom = (string)ConfigHelper.GetDomainSectionValue("emailSettings",
												"registrationFromEmail");
			var vAppOwner = (string)ConfigHelper.GetDomainSectionValue("generalSettings",
												"appOwnerName");

			var vEmailSubject = Resources.EmailTemplates.AccountRecoveryEmailSubject;
			var vEmailBodyTemplate = Resources.EmailTemplates.AccountRecoveryEmailBody;

			var vEmail = pPerson.Email;
			var vToken = DomainMaster.Auth.GetAuthenticationToken(pPerson);
			var vLink = string.Format(pRecoverAccountUrlFormat, vEmail, SecurityHelper.UrlEncode(vToken),
										pPerson.EntityId);
			var vEmailBody = string.Format(vEmailBodyTemplate, vLink, vAppOwner);

			DomainMaster.Auth.ExitPrivelegedAuthorizationMode();

			return EmailHelper.SendTextEmail(vFrom, vEmail, vEmailSubject, vEmailBody);
		}

		/// <summary>
		/// Updates and saves a <c>Person</c> data.
		/// </summary>
		/// <param name="pPersonId">The identifier of the <c>Person</c>.</param>
		/// <param name="pPersonName">The new name of the <c>Person</c>.</param>
		/// <remarks>
		/// <c>NotCriticalDomainException</c>s may be thrown.
		/// <c>InvalidModelPropertiesException</c>s may be thrown.
		/// <c>NotAuthorizedException</c>s may be thrown in case of access control
		/// restrictions.
		/// </remarks>
		/// <exception cref="T:AcademicPlanner.Domain.NotCriticalDomainException">
		/// Thrown with error code <c>DomainErrorCode.C_PERSON_TO_SAVE_NOT_FOUND</c>
		/// if the <c>Person</c> with the given identifier does not exist.
		/// </exception>
		/// <seealso cref="M:GetPerson"/>
		/// <since>1.0</since>
		public void SavePerson(int pPersonId, string pPersonName)
		{
			var vPers = GetPerson(pPersonId);
			if (vPers == null)
				throw new NotCriticalDomainException(DomainErrorCode.C_PERSON_TO_SAVE_NOT_FOUND);

			var vTask = new SavePersonTask(vPers, pPersonName);
			vTask.Run();
		}


		/// <summary>
		/// Updates and saves a <c>Account</c> data; specifically the <c>Account</c>'s
		/// <c>User</c>'s password.
		/// </summary>
		/// <param name="pAccountId">The identifier of the <c>Account</c>.</param>
		/// <param name="pAccountUserPassword">The new password.</param>
		/// <remarks>
		/// <c>NotCriticalDomainException</c>s may be thrown.
		/// <c>InvalidModelPropertiesException</c>s may be thrown.
		/// <c>NotAuthorizedException</c>s may be thrown in case of access control
		/// restrictions.
		/// </remarks>
		/// <exception cref="T:AcademicPlanner.Domain.NotCriticalDomainException">
		/// Thrown with error code <c>DomainErrorCode.C_ACCOUNT_TO_SAVE_NOT_FOUND</c>
		/// if the <c>Account</c> with the given identifier does not exist.
		/// </exception>
		/// <seealso cref="M:GetAccount"/>
		/// <since>1.0</since>
		public void SaveAccount(int pAccountId, string pAccountUserPassword)
		{
			var vAcct = GetAccount(pAccountId);
			if (vAcct == null)
				throw new NotCriticalDomainException(DomainErrorCode.C_ACCOUNT_TO_SAVE_NOT_FOUND);

			var vTask = new SaveAccountTask(vAcct, pAccountUserPassword);
			vTask.Run();
		}

		/// <summary>
		/// Completes the registration of a <c>Person</c> by creating an <c>Account</c>
		/// for him with the supplied <c>User</c> info.
		/// </summary>
		/// <param name="pToken">The verification token (sent out in the registration email).</param>
		/// <param name="pPersonId">The <c>Person</c>'s identifier.</param>
		/// <param name="pEmail">The <c>Person</c>'s email address.</param>
		/// <param name="pPersonName">The <c>Person</c>'s full name.</param>
		/// <param name="pAccountUserName">The username for his <c>Account</c>'s <c>User</c>.</param>
		/// <param name="pAccountUserPassword">The password for his <c>Account</c>'s <c>User</c>.</param>
		/// <remarks>
		/// <c>NotCriticalDomainException</c>s may be thrown in case of biz logic violations
		/// such as a <c>Person</c> already having <c>Account</c>s.
		/// <c>InvalidModelPropertiesException</c>s may be thrown in case of validation errors
		/// such as invalid username or password given.
		/// <c>NotAuthorizedException</c>s may be thrown in case of access control
		/// restrictions.
		/// </remarks>
		/// <since>1.0</since>
		public void SetupPerson(string pToken, int pPersonId, string pEmail,
						string pPersonName, string pAccountUserName, string pAccountUserPassword)
		{
			var vPers = GetPerson(pPersonId);

			DomainMaster.Auth.EnterPrivelegedAuthorizationMode();
			if (vPers == null || !vPers.Email.Equals(pEmail))
			{
				DomainMaster.Auth.ExitPrivelegedAuthorizationMode();
				throw new NotCriticalDomainException(DomainErrorCode.C_PERSON_TO_SETUP_NOT_FOUND);
			}
			else
				DomainMaster.Auth.ExitPrivelegedAuthorizationMode();

			var vTask = new SetupPersonTask(pToken, vPers, pPersonName, pAccountUserName, pAccountUserPassword);
			vTask.Run();
		}

		/// <summary>
		/// Resets a <c>Person</c>'s <c>Account</c> at the given index.
		/// </summary>
		/// <remarks>
		/// Only the password is reset to a new one.
		/// <c>NotCriticalDomainException</c>s may be thrown indirectly.
		/// <c>InvalidModelPropertiesException</c>s may be thrown indirectly.
		/// <c>NotAuthorizedException</c>s may be thrown indirectly.
		/// </remarks>
		/// <param name="pToken">The verification token (sent out in account recovery email).</param>
		/// <param name="pPersonId">The <c>Person</c>'s identifier.</param>
		/// <param name="pEmail">The <c>Person</c>'s email address.</param>
		/// <param name="pAccountIndex">The index of the <c>Account</c> to reset.</param>
		/// <param name="pAccountUserPassword">The new password for <c>Account</c>'s <c>User</c>.</param>
		/// <exception cref="T:AcademicPlanner.Domain.NotCriticalDomainException">
		/// Thrown with error code <c>DomainErrorCode.C_PERSON_TO_RESET_ACCOUNT_NOT_FOUND</c>
		/// if the <c>Person</c> with the given identifier does not exist or the email address
		/// do no match.
		/// </exception>
		/// <since>1.0</since>
		public void ResetPersonAccount(string pToken, int pPersonId, string pEmail, int pAccountIndex,
										string pAccountUserPassword)
		{
			var vPers = GetPerson(pPersonId);

			DomainMaster.Auth.EnterPrivelegedAuthorizationMode();
			if (vPers == null || !vPers.Email.Equals(pEmail))
			{
				DomainMaster.Auth.ExitPrivelegedAuthorizationMode();
				throw new NotCriticalDomainException(DomainErrorCode.C_PERSON_TO_RESET_ACCOUNT_NOT_FOUND);
			}
			else
				DomainMaster.Auth.ExitPrivelegedAuthorizationMode();

			var vTask = new ResetPersonAccountTask(pToken, vPers, pAccountIndex, pAccountUserPassword);
			vTask.Run();
		}

		/// <summary>
		/// Gets a <c>Person</c> awaiting completion of his registration.
		/// This method first ensures that the <paramref name="pToken"/>
		/// is validly associated with the <c>Person</c> of the given
		/// identifier <paramref name="pPersonId"/>, then gets retrieves
		/// the <c>Person</c>.
		/// </summary>
		/// <param name="pToken">The verification token (sent out in the registration email).</param>
		/// <param name="pPersonId">The <c>Person</c>'s identifier.</param>
		/// <param name="pEmail">The <c>Person</c>'s email address.</param>
		/// <returns>
		/// The <c>Person</c> awaiting completion of his registration or <c>null</c>
		/// if a matching <c>Person</c> was not found.
		/// </returns>
		/// <seealso cref="M:AuthMngr.IsAuthenticationTokenValid"/>
		/// <seealso cref="M:GetPerson"/>
		/// <since>1.0</since>
		public Person GetPersonToSetup(string pToken, int pPersonId, string pEmail)
		{
			Person rPers = null;

			DomainMaster.Auth.EnterPrivelegedAuthorizationMode();
			if (DomainMaster.Auth.IsAuthenticationTokenValid(pToken, typeof(Person), pPersonId))
			{
				rPers = GetPerson(pPersonId);
				if (rPers != null && !rPers.Email.Equals(pEmail))
					rPers = null;
			}
			DomainMaster.Auth.ExitPrivelegedAuthorizationMode();

			return rPers;
		}

		/// <summary>
		/// Gets a list of usernames of a <c>Person</c>'s <c>Account</c>s
		/// for resetting.
		/// </summary>
		/// <param name="pToken">The verification token (sent out in the account recovery email).</param>
		/// <param name="pPersonId">The <c>Person</c>'s identifier.</param>
		/// <param name="pEmail">The <c>Person</c>'s email address.</param>
		/// <returns>The list of usernames.</returns>
		/// <seealso cref="M:AuthMngr.IsAuthenticationTokenValid"/>
		/// <seealso cref="M:GetPerson"/>
		/// <since>1.0</since>
		public IList<string> GetAccountUserNamesToReset(string pToken, int pPersonId, string pEmail)
		{
			IList<string> rList = new List<string>();

			DomainMaster.Auth.EnterPrivelegedAuthorizationMode();
			if (DomainMaster.Auth.IsAuthenticationTokenValid(pToken, typeof(Person), pPersonId))
			{
				var vPers = GetPerson(pPersonId);

				if (vPers != null && vPers.Email.Equals(pEmail))
				{
					foreach (var vAcct in vPers.Accounts.ToList())
					{
						rList.Add(vAcct.User.Name);
					}
				}

			}
			DomainMaster.Auth.ExitPrivelegedAuthorizationMode();

			return rList;
		}

		/// <summary>
		/// Gets the <c>Account</c> having the given identifier.
		/// </summary>
		/// <param name="pEntityId">The <c>Account</c>'s identifier.</param>
		/// <returns>The <c>Account</c> with the given identifier or <c>null</c>.</returns>
		/// <seealso cref="P:DomainMaster"/>
		/// <seealso cref="P:AcademicPlanner.Domain.DomainMaster.MainModel"/>
		/// <seealso cref="P:AcademicPlanner.Domain.Models.MainModel.AllAccounts"/>
		/// <seealso cref="M:AcademicPlanner.Domain.Models.Collections.AccountCollection.WithId"/>
		/// <since>1.0</since>
		public Account GetAccount(int pEntityId)
		{
			return DomainMaster.MainModel.AllAccounts.WithId(pEntityId);
		}

		/// <summary>
		/// Gets the <c>Person</c> having the given identifier.
		/// </summary>
		/// <param name="pEntityId">The <c>Person</c>'s identifier.</param>
		/// <returns>The <c>Person</c> with the given identifier or <c>null</c>.</returns>
		/// <since>1.0</since>
		public Person GetPerson(int pEntityId)
		{
			return DomainMaster.MainModel.AllPeople.WithId(pEntityId);
		}

		/// <summary>
		/// Gets the <c>Person</c> having an <c>Account</c>'s <c>User</c> with the given
		/// username.
		/// </summary>
		/// <param name="pAccountUserName">The username.</param>
		/// <returns>
		/// The <c>Person</c> having an <c>Account</c>'s <c>User</c> with the given username.
		/// </returns>
		/// <since>1.0</since>
		public Person GetPerson(string pAccountUserName)
		{
			return DomainMaster.MainModel.AllPeople.WithAccountUserName(pAccountUserName);
		}


		class SaveAccountTask : Task
		{
			Account account;
			string userPassword;

			internal SaveAccountTask(Account pAccount, string pUserPassword)
			{
				account = pAccount;
				userPassword = pUserPassword;
			}

			protected override void Init()
			{
				// check authorization
				DomainMaster.Auth.EnterPrivelegedAuthorizationMode();
				if (!(DomainMaster.Auth.IsUserAuthentic()
					  && account.User.Name.Equals(SessionHelper.GetUserName())))
				{
					DomainMaster.Auth.ExitPrivelegedAuthorizationMode();
					throw new NotAuthorizedException(DomainErrorCode.C_NOT_AUTHORIZED_UPDATE_ACCOUNT);
				}
				else
					DomainMaster.Auth.ExitPrivelegedAuthorizationMode();

				base.Init();
			}

			protected override void Perform()
			{
				var vOp1 = new UpdateAccountOperation(account, userPassword);
				vOp1.Launch();
			}

			protected override void Failed()
			{
				base.Failed();
				account.Refresh();
			}
		}

		class ResetPersonAccountTask : Task
		{
			Person person;
			string token;
			int accountIndex;
			string accountUserPassword;

			internal ResetPersonAccountTask(string pToken, Person pPerson, int pAccountIndex,
											string pAccountUserPassword)
			{
				person = pPerson;
				token = pToken;
				accountIndex = pAccountIndex;
				accountUserPassword = pAccountUserPassword;
			}

			protected override void Init()
			{
				// check authorization
				if (!DomainMaster.Auth.IsAuthenticationTokenValid(token, person))
					throw new NotAuthorizedException(DomainErrorCode.C_NOT_AUTHORIZED_RESET_PERSON_ACCOUNT);

				base.Init();
			}

			protected override void Perform()
			{
				var vAcct = person.Accounts.ToList()[accountIndex];

				if (vAcct == null)
					throw new NotCriticalDomainException(DomainErrorCode.C_ACCOUNT_TO_RESET_NOT_FOUND);

				var vOp1 = new UpdateAccountOperation(vAcct, accountUserPassword);
				vOp1.Launch();
			}

			protected override void Succeeded()
			{
				base.Succeeded();
				// get rid of his AC authentication token
				DomainMaster.Auth.DiscardAuthenticationToken(person);
			}

			protected override void Failed()
			{
				base.Failed();
				person.Refresh();
			}
		}

		class SavePersonTask : Task
		{
			Person person;
			string name;

			internal SavePersonTask(Person pPerson, string pName)
			{
				person = pPerson;
				name = pName;
			}

			protected override void Init()
			{
				// check authorization
				if (!DomainMaster.Auth.IsUserAuthorized(ACOperationName.UPDATE_PERSON, person))
					throw new NotAuthorizedException(DomainErrorCode.C_NOT_AUTHORIZED_UPDATE_PERSON);

				base.Init();
			}

			protected override void Perform()
			{
				var vOp1 = new UpdatePersonOperation(person, name);
				vOp1.Launch();
			}

			protected override void Failed()
			{
				base.Failed();
				person.Refresh();
			}
		}

		class SetupPersonTask : Task
		{
			Person person;
			string token;
			string name;
			string accountUserName;
			string accountPassword;

			public SetupPersonTask(string pToken, Person pPerson, string pName, string pAccountUserName,
									string pAccountPassword)
			{
				token = pToken;
				person = pPerson;
				name = pName;
				accountUserName = pAccountUserName;
				accountPassword = pAccountPassword;
			}

			protected override void Init()
			{
				// check authorization
				if (!DomainMaster.Auth.IsAuthenticationTokenValid(token, person))
					throw new NotAuthorizedException(DomainErrorCode.C_NOT_AUTHORIZED_SETUP_PERSON);
				if (!DomainMaster.Auth.IsUserAuthorized(ACOperationName.CREATE_ACCOUNT))
					throw new NotAuthorizedException(DomainErrorCode.C_NOT_AUTHORIZED_CREATE_ACCOUNT);

				base.Init();
			}

			protected override void Perform()
			{
				// logic error checking
				if (person.Accounts.ToList().Count() > 0)
					throw new NotCriticalDomainException(DomainErrorCode.C_PERSON_ALREADY_SETUP);

				// update his data
				var vOp1 = new UpdatePersonOperation(person, name);
				vOp1.Launch();
				// create his initial account
				var vOp2 = new CreateAccountOperation(person, accountUserName, accountPassword);
				try
				{
					vOp2.Launch();
				}
				catch (InvalidModelPropertiesException vIx)
				{
					var vErrs = new InvalidModelPropertiesErrorCollection("Person");
					vErrs.AddInner("Account", vIx.Errors);
					throw new InvalidModelPropertiesException(DomainErrorCode.C_INVALID_PERSON_ACCOUNT_PROPERTIES,
																vErrs);
				}
				// create a planner actor for his new account
				var vOp3 = new AcademicPlanningMngr.CreatePlannerOperation(vOp2.Account.User);
				vOp3.Launch();
			}

			protected override void Succeeded()
			{
				base.Succeeded();
				var vUser = person.Accounts.ToList().First().User;

				// setup the new person for Access Control
				DomainMaster.Auth.AssignUserRole(vUser, ACRoleName.PLANNER);
				DomainMaster.Auth.RegisterIntoACSystem(person, vUser);
				DomainMaster.Auth.EnterPrivelegedAuthorizationMode();
				DomainMaster.Auth.RegisterIntoACSystem(DomainMaster.Planning.GetPlanner(vUser.Name).Plan, vUser);
				DomainMaster.Auth.ExitPrivelegedAuthorizationMode();
				// get rid of his AC authentication token
				DomainMaster.Auth.DiscardAuthenticationToken(person);
			}

			protected override void Failed()
			{
				base.Failed();
				DomainMaster.ModelsFountain.LoseModel(person);
			}
		}

		class RegisterTask : Task
		{
			string email;
			string compleRegistrationUrlFormat;

			internal RegisterTask(string pEmail, string pCompleRegistrationUrlFormat)
			{
				email = pEmail;
				compleRegistrationUrlFormat = pCompleRegistrationUrlFormat;
			}

			protected override void Init()
			{
				// check authorization
				if (!DomainMaster.Auth.IsUserAuthorized(ACOperationName.CREATE_PERSON))
					throw new NotAuthorizedException(DomainErrorCode.C_NOT_AUTHORIZED_CREATE_PERSON);

				base.Init();
			}

			protected override void Perform()
			{
				var vOp = new CreatePersonOperation(email);

				// create person
				vOp.Launch();
				// send registration email
				if (!SendRegistrationEmail(vOp.Person))
					throw new NotCriticalDomainException(DomainErrorCode.C_REGISTRATION_EMAIL_FAILURE);
			}

			bool SendRegistrationEmail(Person pPerson)
			{
				DomainMaster.Auth.EnterPrivelegedAuthorizationMode();

				var vRegistrationEmailFrom = (string)ConfigHelper.GetDomainSectionValue("emailSettings",
													"registrationFromEmail");
				var vAppOwner = (string)ConfigHelper.GetDomainSectionValue("generalSettings",
													"appOwnerName");

				var vRegistrationEmailSubject = Resources.EmailTemplates.RegistrationEmailSubject;
				var vRegistrationEmailBodyTemplate = Resources.EmailTemplates.RegistrationEmailBody;

				var vEmail = pPerson.Email;
				var vToken = DomainMaster.Auth.GetAuthenticationToken(pPerson);
				var vRegistrationEmailLink = string.Format(compleRegistrationUrlFormat, vEmail,
															SecurityHelper.UrlEncode(vToken), pPerson.EntityId);
				var vRegistrationEmailBody = string.Format(vRegistrationEmailBodyTemplate,
															vRegistrationEmailLink, vAppOwner);

				DomainMaster.Auth.ExitPrivelegedAuthorizationMode();

				return EmailHelper.SendTextEmail(vRegistrationEmailFrom, vEmail,
													vRegistrationEmailSubject, vRegistrationEmailBody);
			}
		}


		class UpdateAccountOperation : Operation
		{
			protected Account account;
			protected bool? activated;
			protected string userName;
			protected string userPassword;

			internal UpdateAccountOperation(Account pAcct, string pUserPswd)
			{
				account = pAcct;
				userPassword = pUserPswd;
			}

			protected override void Execute()
			{
				account.Set(activated, userName, userPassword);
			}
		}

		class CreateAccountOperation : UpdateAccountOperation
		{
			Person person;

			internal Account Account { get { return account; } }

			internal CreateAccountOperation(Person pPerson, string pAccountUserName, string pAccountPassword)
				: base(null, pAccountPassword)
			{
				person = pPerson;
				activated = true;
				userName = pAccountUserName;
				userPassword = pAccountPassword;
			}

			protected override void Execute()
			{
				account = DomainMaster.MainModel.AllAccounts.WithUserName(userName);
				if (account != null)
					throw new InvalidModelPropertiesException("User", "Name",
							InvalidModelPropertyErrorCode.NOT_UNIQUE,
							DomainErrorCode.C_NOT_UNIQUE_ACCOUNT_USER_NAME);

				account = DomainMaster.ModelsFountain.GetNewAccount();
				base.Execute();
				person.AddAccount(account);
				account.Persit();
			}
		}

		class UpdatePersonOperation : Operation
		{
			protected Person person;
			protected string name;
			protected string email;

			internal UpdatePersonOperation(Person pPers, string pName)
			{
				person = pPers;
				name = pName;
			}

			protected override void Execute()
			{
				// set properties
				person.Set(email, name);
				// persit to storage
				person.Persit();
			}
		}

		class CreatePersonOperation : UpdatePersonOperation
		{
			internal Person Person { get { return person; } }

			internal CreatePersonOperation(string pEmail)
				: base(null, null)
			{
				email = pEmail;
			}

			protected override void Execute()
			{
				person = DomainMaster.MainModel.AllPeople.WithEmail(email);
				DomainMaster.Auth.EnterPrivelegedAuthorizationMode();
				// make sure email is unique among completely registered people
				if (person != null && !person.Name.Equals(string.Empty))
				{
					DomainMaster.Auth.ExitPrivelegedAuthorizationMode();
					throw new InvalidModelPropertiesException("Person", "Email",
							InvalidModelPropertyErrorCode.NOT_UNIQUE,
							DomainErrorCode.C_NOT_UNIQUE_PERSON_EMAIL);
				}
				else if (person == null)
				{
					person = DomainMaster.ModelsFountain.GetNewPerson();
					base.Execute();
				}
				DomainMaster.Auth.ExitPrivelegedAuthorizationMode();
			}
		}

	}
}
